import test from "ava";
import ceno from "../src";
import { HttpError, TimeoutError } from "../src";
import { createHttpTestServer } from "./helpers/create-http-test-server";

test('HttpError - 基本属性', async (t) => {
    t.plan(5)
    const server = await createHttpTestServer()

    server.get('/404', (request, response) => {
        response.status(404).json({ error: 'Not Found' })
    })

    const error = await t.throwsAsync(
        () => ceno.get(server.url + '/404'),
        { instanceOf: HttpError }
    )

    t.is(error.name, 'HttpError')
    t.is(error.response.status, 404)
    t.true(error.message.includes('404'))
    t.truthy(error.request)

    await server.close()
})

test('HttpError - 不同状态码', async (t) => {
    const server = await createHttpTestServer()
    const statusCodes = [400, 401, 403, 404, 500, 502, 503, 504]

    for (const status of statusCodes) {
        server.get(`/${status}`, (request, response) => {
            response.status(status).json({ error: `Error ${status}` })
        })

        const error = await t.throwsAsync(
            () => ceno.get(server.url + `/${status}`),
            { instanceOf: HttpError }
        )

        t.is(error.response.status, status)
        t.true(error.message.includes(status.toString()))
    }

    await server.close()
})

test('HttpError - throwHttpErrors: true (默认)', async (t) => {
    t.plan(2)
    const server = await createHttpTestServer()

    server.get('/error', (request, response) => {
        response.status(500).json({ error: 'Server Error' })
    })

    const error = await t.throwsAsync(
        () => ceno.get(server.url + '/error'), // 默认 throwHttpErrors: true
        { instanceOf: HttpError }
    )

    t.is(error.response.status, 500)

    await server.close()
})

test('HttpError - throwHttpErrors: false 不抛出错误', async (t) => {
    t.plan(2)
    const server = await createHttpTestServer()

    server.get('/error', (request, response) => {
        response.status(500).json({ error: 'Server Error' })
    })

    const response = await ceno.get(
        server.url + '/error',
        { throwHttpErrors: false }
    )

    t.is(response.status, 500)
    t.false(response.ok)

    await server.close()
})

test('HttpError - 成功响应不抛出错误', async (t) => {
    const server = await createHttpTestServer()

    server.get('/success', (request, response) => {
        response.json({ success: true })
    })

    const data = await ceno.get(server.url + '/success').json()
    t.deepEqual(data, { success: true })

    await server.close()
})

test('TimeoutError - 基本超时', async (t) => {
    const server = await createHttpTestServer()

    server.get('/timeout', (request, response) => {
        setTimeout(() => {
            response.end('Hello Ceno')
        }, 2000)
    })

    const error = await t.throwsAsync(
        () => ceno.get(server.url + '/timeout', { timeout: 500 }),
        { instanceOf: TimeoutError }
    )

    t.is(error.name, 'TimeoutError')
    t.is(error.message, 'Request timed out')
    t.truthy(error.request)

    await server.close()
})

test('TimeoutError - 不同超时值', async (t) => {
    const server = await createHttpTestServer()

    server.get('/slow', (request, response) => {
        setTimeout(() => {
            response.end('Hello Ceno')
        }, 1000)
    })

    // 短超时应该失败
    await t.throwsAsync(
        () => ceno.get(server.url + '/slow', { timeout: 200 }),
        { instanceOf: TimeoutError }
    )

    // 长超时应该成功
    const response = await ceno.get(server.url + '/slow', { timeout: 2000 })
    t.is(response.status, 200)

    await server.close()
})

test('TimeoutError - timeout: false 禁用超时', async (t) => {
    const server = await createHttpTestServer()

    server.get('/slow', (request, response) => {
        setTimeout(() => {
            response.end('Hello Ceno')
        }, 500)
    })

    const response = await ceno.get(server.url + '/slow', { timeout: false })
    t.is(response.status, 200)

    await server.close()
})

test('RangeError - 超时值过大', async (t) => {
    await t.throwsAsync(
        () => ceno.get('http://example.com', { timeout: 2_147_483_648 }),
        {
            instanceOf: RangeError,
            message: /The.*option cannot be greater than/
        }
    )
})

test('RangeError - 最大安全超时值', async (t) => {
    // 最大安全值应该可以工作（不会实际发送请求，只测试不抛出 RangeError）
    t.throws(
        () => ceno.get('http://localhost:99999', { timeout: 2_147_483_647 }),
        {
            // 应该抛出网络错误，而不是 RangeError
            instanceOf: TypeError,
            message: /Failed to parse URL/
        }
    )
})

test('TypeError - 无效 input 类型', async (t) => {
    t.throws(
        // @ts-expect-error - 故意传递无效类型用于测试
        () => ceno.get(123),
        {
            instanceOf: TypeError,
            message: '`input` must be a string, URL, or Request'
        }
    )
})

test('TypeError - onDownloadProgress 不是函数', async (t) => {
    await t.throwsAsync(
        () => ceno.get('http://example.com', {
            // @ts-expect-error - 故意传递无效类型用于测试
            onDownloadProgress: 'not a function'
        }),
        {
            instanceOf: TypeError,
            message: 'The `onDownloadProgress` option must be a function'
        }
    )
})

test('TypeError - onUploadProgress 不是函数', async (t) => {
    t.throws(
        () => ceno.post('http://example.com', {
            json: { test: 'data' },
            // @ts-expect-error - 故意传递无效类型用于测试
            onUploadProgress: 'not a function'
        }),
        {
            instanceOf: TypeError,
            message: 'The `onUploadProgress` option must be a function'
        }
    )
})

test('Error - prefixUrl 与斜杠开头的 input', async (t) => {
    t.throws(
        () => ceno.get('/path', { prefixUrl: 'http://example.com' }),
        {
            instanceOf: Error,
            message: '`input` must not begin with a slash when using `prefixUrl`'
        }
    )
})

test('Error - retry.methods 不是数组', async (t) => {
    t.throws(
        () => ceno.get('http://example.com', {
            retry: {
                // @ts-expect-error - 故意传递无效类型用于测试
                methods: 'get',
                limit: 1
            }
        }),
        {
            instanceOf: Error,
            message: 'retry.methods must be an array'
        }
    )
})

test('Error - retry.statusCodes 不是数组', async (t) => {
    t.throws(
        () => ceno.get('http://example.com', {
            retry: {
                // @ts-expect-error - 故意传递无效类型用于测试
                statusCodes: 500,
                limit: 1
            }
        }),
        {
            instanceOf: Error,
            message: 'retry.statusCodes must be an array'
        }
    )
})

test('重试错误 - 达到重试限制抛出原始错误', async (t) => {
    const server = await createHttpTestServer()

    server.get('/retry-fail', (request, response) => {
        response.status(500).json({ error: 'Server Error' })
    })

    const error = await t.throwsAsync(
        () => ceno.get(server.url + '/retry-fail', {
            retry: {
                limit: 2,
                methods: ['get'],
                statusCodes: [500]
            }
        }),
        { instanceOf: HttpError }
    )

    t.is(error.response.status, 500)

    await server.close()
})

test('重试错误 - 413 状态码不重试', async (t) => {
    const server = await createHttpTestServer()

    server.get('/payload-too-large', (request, response) => {
        response.status(413).json({ error: 'Payload Too Large' })
    })

    const error = await t.throwsAsync(
        () => ceno.get(server.url + '/payload-too-large', {
            retry: {
                limit: 3,
                methods: ['get'],
                statusCodes: [413] // 即使配置了413，也不会重试
            }
        }),
        { instanceOf: HttpError }
    )

    t.is(error.response.status, 413)

    await server.close()
})

test('重试错误 - 不在重试状态码列表中的错误', async (t) => {
    const server = await createHttpTestServer()

    server.get('/not-retryable', (request, response) => {
        response.status(404).json({ error: 'Not Found' })
    })

    const error = await t.throwsAsync(
        () => ceno.get(server.url + '/not-retryable', {
            retry: {
                limit: 3,
                methods: ['get'],
                statusCodes: [500] // 404 不在重试列表中
            }
        }),
        { instanceOf: HttpError }
    )

    t.is(error.response.status, 404)

    await server.close()
})

test('重试错误 - TimeoutError 不重试', async (t) => {
    const server = await createHttpTestServer()

    server.get('/timeout-no-retry', (request, response) => {
        setTimeout(() => {
            response.end('Hello Ceno')
        }, 2000)
    })

    const error = await t.throwsAsync(
        () => ceno.get(server.url + '/timeout-no-retry', {
            timeout: 500,
            retry: {
                limit: 3,
                methods: ['get'],
                statusCodes: [500]
            }
        }),
        { instanceOf: TimeoutError }
    )

    t.is(error.name, 'TimeoutError')

    await server.close()
})

test('重试错误 - Retry-After 头部处理', async (t) => {
    const server = await createHttpTestServer()

    let requestCount = 0
    server.get('/retry-after', (request, response) => {
        requestCount++
        if (requestCount === 1) {
            response.set('Retry-After', '1') // 1秒后重试
            response.status(429).json({ error: 'Too Many Requests' })
        } else {
            response.json({ success: true, requestCount })
        }
    })

    const startTime = Date.now()
    const data = await ceno.get(server.url + '/retry-after', {
        retry: {
            limit: 2,
            methods: ['get'],
            statusCodes: [429],
            afterStatusCodes: [429]
        }
    }).json()

    const elapsedTime = Date.now() - startTime
    
    t.deepEqual(data, { success: true, requestCount: 2 })
    t.true(elapsedTime >= 1000) // 应该至少等待1秒

    await server.close()
})

test('错误 - 网络错误与超时组合', async (t) => {
    // 测试无法连接的地址
    t.throws(
        () => ceno.get('http://localhost:99999', { timeout: 1000 }),
        { instanceOf: TypeError } // URL解析错误
    )
})

test('错误 - 自定义 fetch 函数抛出错误', async (t) => {
    await t.throwsAsync(
        () => ceno.get('http://example.com', {
            fetch: () => {
                throw new Error('Custom fetch error')
            }
        }),
        { message: 'Custom fetch error' }
    )
})

test('错误 - 拦截器中的错误处理', async (t) => {
    const server = await createHttpTestServer()

    server.get('/interceptor-error', (request, response) => {
        response.json({ success: true })
    })

    await t.throwsAsync(
        () => ceno.get(server.url + '/interceptor-error', {
            interceptors: {
                request: [{
                    onFulfilled: (request) => {
                        throw new Error('Request interceptor error')
                    }
                }]
            }
        }),
        { message: 'Request interceptor error' }
    )

    await server.close()
})

test('错误 - 响应拦截器中的错误', async (t) => {
    const server = await createHttpTestServer()

    server.get('/response-interceptor-error', (request, response) => {
        response.json({ success: true })
    })

    await t.throwsAsync(
        () => ceno.get(server.url + '/response-interceptor-error', {
            interceptors: {
                response: [{
                    onFulfilled: (response) => {
                        throw new Error('Response interceptor error')
                    }
                }]
            }
        }),
        { message: 'Response interceptor error' }
    )

    await server.close()
})